//
//  UnkeyedCodingContainer.swift
//  MoonKit
//
//  Created by moon on 2019/3/5.
//  Copyright © 2019 moon. All rights reserved.
//

import Foundation
public protocol UnkeyedCodingContainer {
	var codingOperation:CodingOperation { get }
	var codingPath: [CodingKey] { get }
	var count: Int { get }
	var isAtEnd: Bool { get }
	var currentIndex: Int { get set }
	
	/// 返回当前索引对应的keyed容器
	///	成功时当前索引+1
	///
	/// - parameter type: Key类型
	/// - Returns: 嵌套的keyed容器
	/// - Throws: `CodingError.indexOutOfRange` : 当前索引超过范围
	/// - Throws: `CodingError.typeMismatch` : key对应的类型不为keyed容器
	mutating func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedCodingContainer<NestedKey> where NestedKey : CodingKey
	
	/// 返回当前索引对应的unkeyed容器
	///	成功时当前索引+1
	///
	/// - Returns: 嵌套的unkeyed容器
	/// - Throws: `CodingError.indexOutOfRange` : 当前索引超过范围
	/// - Throws: `CodingError.typeMismatch` : key对应的类型不为unkeyed容器
	mutating func nestedUnkeyedContainer() throws -> UnkeyedCodingContainer
	
	/// 编码时,构造一个Coder,Coder的容器,指向当前索引对应的值.
	///	解码时,使用当前索引对应的值,构造一个Coder
	///	成功时当前索引+1
	///
	/// - returns: 索引对应值构造的Coder
	mutating func superCoder() throws -> Coder
	
	/// 编码nil
	///	成功时当前索引+1
	///
	/// - throws: `CodingError.invalidValue`: 当前容器不支持编码nil
	mutating func encodeNil() throws
	
	/// 编码对象到当前索引中
	///	成功时当前索引+1
	///
	/// - parameter value: 被编码的对象
	/// - throws: `CodingError.invalidValue`: 当前容器不支持编码此对象
	/// - Throws: `CodingError.indexOutOfRange` : 当前索引超过范围
	mutating func encode<T>(_ value: T) throws where T : Codable
	
	/// 按顺序,编码序列中的对象.每次编码成功,索引+1
	///
	/// - parameter sequence: 被编码的系列对象
	/// - throws: `CodingError.invalidValue`: 当前容器不支持编码此对象
	/// - Throws: `CodingError.indexOutOfRange` : 当前索引超过范围
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element : Codable
	
	mutating func encode(_ value: Bool) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Bool
	
	mutating func encode(_ value: String) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == String
	
	mutating func encode(_ value: Double) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Double
	
	mutating func encode(_ value: Float) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Float
	
	mutating func encode(_ value: Int) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Int
	
	mutating func encode(_ value: Int8) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Int8
	
	mutating func encode(_ value: Int16) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Int16
	
	mutating func encode(_ value: Int32) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Int32
	
	mutating func encode(_ value: Int64) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Int64
	
	mutating func encode(_ value: UInt) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == UInt
	
	mutating func encode(_ value: UInt8) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == UInt8
	
	mutating func encode(_ value: UInt16) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == UInt16
	
	mutating func encode(_ value: UInt32) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == UInt32
	
	mutating func encode(_ value: UInt64) throws
	mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == UInt64
	
	
	/// 解码nil
	///	成功时当前索引+1
	///
	/// - returns: 索引对应的值是否为nil
	/// - Throws: `CodingError.indexOutOfRange` : 当前索引超过范围
	mutating func decodeNil() throws -> Bool

	/// 解码当前索引的值为指定类型
	///	成功时当前索引+1
	///
	/// - parameter type: 要解码出来的类型
	/// - returns: 被解码出来的对象
	/// - throws: `CodingError.typeMismatch` : 类型不符合.
	/// - Throws: `CodingError.indexOutOfRange` : 当前索引超过范围
	mutating func decode<T : Codable>(_ type: T.Type) throws -> T
	
	/// 解码当前索引的值,当值为nil时返回nil,否则返回解码出来的对象
	///	成功时当前索引+1
	///
	/// - parameter type: 要解码出来的类型
	/// - returns: 索引对应的值为nil时,返回nil,否则返回被解码出来的对象
	/// - throws: `CodingError.typeMismatch` : 类型不符合.
	/// - Throws: `CodingError.indexOutOfRange` : 当前索引超过范围
	mutating func decodeIfPresent<T : Codable>(_ type: T.Type) throws -> T?
	
	mutating func decode(_ type: Bool.Type) throws -> Bool
	mutating func decodeIfPresent(_ type: Bool.Type) throws -> Bool?
	
	mutating func decode(_ type: String.Type) throws -> String
	mutating func decodeIfPresent(_ type: String.Type) throws -> String?
	
	mutating func decode(_ type: Double.Type) throws -> Double
	mutating func decodeIfPresent(_ type: Double.Type) throws -> Double?
	
	mutating func decode(_ type: Float.Type) throws -> Float
	mutating func decodeIfPresent(_ type: Float.Type) throws -> Float?
	
	mutating func decode(_ type: Int.Type) throws -> Int
	mutating func decodeIfPresent(_ type: Int.Type) throws -> Int?
	
	mutating func decode(_ type: Int8.Type) throws -> Int8
	mutating func decodeIfPresent(_ type: Int8.Type) throws -> Int8?
	
	mutating func decode(_ type: Int16.Type) throws -> Int16
	mutating func decodeIfPresent(_ type: Int16.Type) throws -> Int16?
	
	mutating func decode(_ type: Int32.Type) throws -> Int32
	mutating func decodeIfPresent(_ type: Int32.Type) throws -> Int32?
	
	mutating func decode(_ type: Int64.Type) throws -> Int64
	mutating func decodeIfPresent(_ type: Int64.Type) throws -> Int64?
	
	mutating func decode(_ type: UInt.Type) throws -> UInt
	mutating func decodeIfPresent(_ type: UInt.Type) throws -> UInt?
	
	mutating func decode(_ type: UInt8.Type) throws -> UInt8
	mutating func decodeIfPresent(_ type: UInt8.Type) throws -> UInt8?
	
	mutating func decode(_ type: UInt16.Type) throws -> UInt16
	mutating func decodeIfPresent(_ type: UInt16.Type) throws -> UInt16?
	
	mutating func decode(_ type: UInt32.Type) throws -> UInt32
	mutating func decodeIfPresent(_ type: UInt32.Type) throws -> UInt32?
	
	mutating func decode(_ type: UInt64.Type) throws -> UInt64
	mutating func decodeIfPresent(_ type: UInt64.Type) throws -> UInt64?
	
}

extension UnkeyedCodingContainer {
	public var isAtEnd:Bool {
		return currentIndex >= self.count
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Bool {
		for element in sequence {
			try self.encode(element)
		}
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == String {
		for element in sequence {
			try self.encode(element)
		}
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Double {
		for element in sequence {
			try self.encode(element)
		}
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Float {
		for element in sequence {
			try self.encode(element)
		}
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Int {
		for element in sequence {
			try self.encode(element)
		}
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Int8 {
		for element in sequence {
			try self.encode(element)
		}
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Int16 {
		for element in sequence {
			try self.encode(element)
		}
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Int32 {
		for element in sequence {
			try self.encode(element)
		}
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == Int64 {
		for element in sequence {
			try self.encode(element)
		}
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == UInt {
		for element in sequence {
			try self.encode(element)
		}
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == UInt8 {
		for element in sequence {
			try self.encode(element)
		}
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == UInt16 {
		for element in sequence {
			try self.encode(element)
		}
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == UInt32 {
		for element in sequence {
			try self.encode(element)
		}
	}
	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element == UInt64 {
		for element in sequence {
			try self.encode(element)
		}
	}

	
	public mutating func encode<T>(contentsOf sequence: T) throws where T : Sequence, T.Element : Codable {
		for element in sequence {
			try self.encode(element)
		}
	}
}

extension UnkeyedCodingContainer {
	
	@inlinable
	public mutating func decodeIfPresent(_ type: Bool.Type) throws -> Bool? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable
	public mutating func decodeIfPresent(_ type: String.Type) throws -> String? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable
	public mutating func decodeIfPresent(_ type: Double.Type) throws -> Double? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable
	public mutating func decodeIfPresent(_ type: Float.Type) throws -> Float? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable
	public mutating func decodeIfPresent(_ type: Int.Type) throws -> Int? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable
	public mutating func decodeIfPresent(_ type: Int8.Type) throws -> Int8? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable
	public mutating func decodeIfPresent(_ type: Int16.Type) throws -> Int16? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable
	public mutating func decodeIfPresent(_ type: Int32.Type) throws -> Int32? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable
	public mutating func decodeIfPresent(_ type: Int64.Type) throws -> Int64? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable
	public mutating func decodeIfPresent(_ type: UInt.Type) throws -> UInt? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable
	public mutating func decodeIfPresent(_ type: UInt8.Type) throws -> UInt8? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable
	public mutating func decodeIfPresent(_ type: UInt16.Type) throws -> UInt16? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable
	public mutating func decodeIfPresent(_ type: UInt32.Type) throws -> UInt32? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable
	public mutating func decodeIfPresent(_ type: UInt64.Type) throws -> UInt64? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
	
	@inlinable // FIXME(sil-serialize-all)
	public mutating func decodeIfPresent<T:Codable>(_ type: T.Type) throws -> T? {
		guard try !self.isAtEnd && !self.decodeNil() else { return nil }
		return try self.decode(type)
	}
}


extension UnkeyedCodingContainer {
	
	/// 编码对象到当前索引中
	///	成功时当前索引+1
	/// 如果T为以下类型:['Bool', 'String', 'Double', 'Float','Int', 'Int8', 'Int16', 'Int32', 'Int64','UInt', 'UInt8', 'UInt16', 'UInt32', 'UInt64'],则会调用具体类型对应的方法,否则调用泛型的T方法
	///
	/// - parameter value: 被编码的对象
	/// - throws: `CodingError.invalidValue`: 当前容器不支持编码此对象
	/// - Throws: `CodingError.indexOutOfRange` : 当前索引超过范围
	public mutating func encodeCodableValue<T>(_ value: T) throws where T : Codable {
		switch value {
		case let v as Bool:
			try encode(v)
		case let v as String:
			try encode(v)
		case let v as Double:
			try encode(v)
		case let v as Float:
			try encode(v)
		case let v as Int:
			try encode(v)
		case let v as Int8:
			try encode(v)
		case let v as Int16:
			try encode(v)
		case let v as Int32:
			try encode(v)
		case let v as Int64:
			try encode(v)
		case let v as UInt:
			try encode(v)
		case let v as UInt8:
			try encode(v)
		case let v as UInt16:
			try encode(v)
		case let v as UInt32:
			try encode(v)
		case let v as UInt64:
			try encode(v)
		default:
			try encode(value)
		}
	}
	
	/// 按顺序,编码序列中的对象.每次编码成功,索引+1
	/// 如果T为以下类型:['Bool', 'String', 'Double', 'Float','Int', 'Int8', 'Int16', 'Int32', 'Int64','UInt', 'UInt8', 'UInt16', 'UInt32', 'UInt64'],则会调用具体类型对应的方法,否则调用泛型的T方法
	///
	/// - parameter sequence: 被编码的系列对象
	/// - throws: `CodingError.invalidValue`: 当前容器不支持编码此对象
	/// - Throws: `CodingError.indexOutOfRange` : 当前索引超过范围
	public mutating func encodeCodableValue<T>(contentsOf sequence: T) throws where T : Sequence, T.Element : Codable {
		for element in sequence {
			try self.encodeCodableValue(element)
		}
	}

	/// 解码当前索引的值为指定类型
	///	成功时当前索引+1
	/// 如果T为以下类型:['Bool', 'String', 'Double', 'Float','Int', 'Int8', 'Int16', 'Int32', 'Int64','UInt', 'UInt8', 'UInt16', 'UInt32', 'UInt64'],则会调用具体类型对应的方法,否则调用泛型的T方法
	///
	/// - parameter type: 要解码出来的类型
	/// - returns: 被解码出来的对象
	/// - throws: `CodingError.typeMismatch` : 类型不符合.
	/// - Throws: `CodingError.indexOutOfRange` : 当前索引超过范围
	public mutating func decodeCodableValue<T : Codable>(_ type: T.Type) throws -> T {
		switch type {
		case let t as Bool.Type:
			return try decode(t) as! T
		case let t as String.Type:
			return try decode(t) as! T
		case let t as Double.Type:
			return try decode(t) as! T
		case let t as Float.Type:
			return try decode(t) as! T
		case let t as Int.Type:
			return try decode(t) as! T
		case let t as Int8.Type:
			return try decode(t) as! T
		case let t as Int16.Type:
			return try decode(t) as! T
		case let t as Int32.Type:
			return try decode(t) as! T
		case let t as Int64.Type:
			return try decode(t) as! T
		case let t as UInt.Type:
			return try decode(t) as! T
		case let t as UInt8.Type:
			return try decode(t) as! T
		case let t as UInt16.Type:
			return try decode(t) as! T
		case let t as UInt32.Type:
			return try decode(t) as! T
		case let t as UInt64.Type:
			return try decode(t) as! T
		default:
			return try decode(type)
		}
	}
	
	/// 解码当前索引的值,当值为nil时返回nil,否则返回解码出来的对象
	///	成功时当前索引+1
	/// 如果T为以下类型:['Bool', 'String', 'Double', 'Float','Int', 'Int8', 'Int16', 'Int32', 'Int64','UInt', 'UInt8', 'UInt16', 'UInt32', 'UInt64'],则会调用具体类型对应的方法,否则调用泛型的T方法
	///
	/// - parameter type: 要解码出来的类型
	/// - returns: 索引对应的值为nil时,返回nil,否则返回被解码出来的对象
	/// - throws: `CodingError.typeMismatch` : 类型不符合.
	/// - Throws: `CodingError.indexOutOfRange` : 当前索引超过范围
	public mutating func decodeCodableValueIfPresent<T : Codable>(_ type: T.Type) throws -> T? {
		switch type {
		case let t as Bool.Type:
			return try decodeIfPresent(t) as! T?
		case let t as String.Type:
			return try decodeIfPresent(t) as! T?
		case let t as Double.Type:
			return try decodeIfPresent(t) as! T?
		case let t as Float.Type:
			return try decodeIfPresent(t) as! T?
		case let t as Int.Type:
			return try decodeIfPresent(t) as! T?
		case let t as Int8.Type:
			return try decodeIfPresent(t) as! T?
		case let t as Int16.Type:
			return try decodeIfPresent(t) as! T?
		case let t as Int32.Type:
			return try decodeIfPresent(t) as! T?
		case let t as Int64.Type:
			return try decodeIfPresent(t) as! T?
		case let t as UInt.Type:
			return try decodeIfPresent(t) as! T?
		case let t as UInt8.Type:
			return try decodeIfPresent(t) as! T?
		case let t as UInt16.Type:
			return try decodeIfPresent(t) as! T?
		case let t as UInt32.Type:
			return try decodeIfPresent(t) as! T?
		case let t as UInt64.Type:
			return try decodeIfPresent(t) as! T?
		default:
			return try decodeIfPresent(type)
		}
	}
}
